import serial
import binascii

class ResponsePackage(object):
	"""docstring for ResponsePackage"""
	def __init__(self, 
			sid = 0,
			did = 0,
			rcm = 0,
			ret = 0,
			data = '',
			packageString = ''):
		super(ResponsePackage, self).__init__()
		if (len(packageString) == 52):
			if not checkSum(packageString) : raise Exception("Checksum validation not valid!\r\nComposing data: " + packageString)
			self.sid = parseByte(packageString[2 * 2 : 3 * 2])
			self.did = parseByte(packageString[3 * 2 : 4 * 2])
			self.rcm = parseWord(packageString[4 * 2 : 6 * 2])
			self.len = min(parseWord(packageString[6 * 2 : 8 * 2]), 16)
			self.ret = parseWord(packageString[8 * 2 : 10 * 2])
			self.data = packageString[10 * 2 : 10 * 2 + (self.len - 2) * 2]
		else:
			self.sid = sid
			self.did = did
			self.rcm = rcm
			self.len = min(len(data) / 2 + 2, 16)
			self.ret = ret
			self.data = data[0 : (self.len - 2) * 2]

	def getPackageString(self):
		prefix = 'AA55'
		sid = makeByte(self.sid)
		did = makeByte(self.did)

		data = self.data
		data = data + '0000000000000000000000000000'[(self.len - 2) * 2:]

		pkgData = prefix + sid + did + makeWord(self.rcm) + makeWord(self.len) + makeWord(self.ret) + data

		pkgData = pkgData + calcChkSum(pkgData)
		return pkgData

	def getPackageData(self):
		return binascii.unhexlify(self.getPackageString())

class ResponseDataPackage(object):
	"""docstring for ResponseDataPackage"""
	def __init__(self, 
			sid = 0,
			did = 0,
			rcm = 0,
			ret = 0,
			data = '',
			packageString = ''):
		super(ResponseDataPackage, self).__init__()
		if (len(packageString) > 0):
			if not checkSum(packageString) : raise Exception("Checksum validation not valid!\r\nComposing data: " + packageString)
			self.sid = parseByte(packageString[2 * 2 : 3 * 2])
			self.did = parseByte(packageString[3 * 2 : 4 * 2])
			self.rcm = parseWord(packageString[4 * 2 : 6 * 2])
			self.len = min(parseWord(packageString[6 * 2 : 8 * 2]), 499)
			self.ret = parseWord(packageString[8 * 2 : 10 * 2])
			self.data = packageString[10 * 2 : 10 * 2 + (self.len - 2) * 2]
		else:
			self.sid = sid
			self.did = did
			self.rcm = rcm
			self.len = min(len(data) / 2 + 2, 499)
			self.ret = ret
			self.data = data[0 : (self.len - 2) * 2]

	def getPackageString(self):
		prefix = 'A55A'
		sid = makeByte(self.sid)
		did = makeByte(self.did)

		data = self.data

		pkgData = prefix + sid + did + makeWord(self.rcm) + makeWord(self.len) + makeWord(self.ret) + data

		pkgData = pkgData + calcChkSum(pkgData)
		return pkgData

	def getPackageData(self):
		return binascii.unhexlify(self.getPackageString())


class CommandPackage(object):
	"""docstring for CommandPackage"""
	def __init__(self, 
			sid = 0,
			did = 0,
			cmd = 0,
			data = '',
			packageString = ''):
		super(CommandPackage, self).__init__()
		if (len(packageString) == 52):
			if not checkSum(packageString) : raise Exception("Checksum validation not valid!\r\nComposing data: " + packageString)
			self.sid = parseByte(packageString[2 * 2 : 3 * 2])
			self.did = parseByte(packageString[3 * 2 : 4 * 2])
			self.cmd = parseWord(packageString[4 * 2 : 6 * 2])
			self.len = min(parseWord(packageString[6 * 2 : 8 * 2]), 16)
			self.data = packageString[8 * 2 : 8 * 2 + self.len * 2]
		else:
			self.sid = sid
			self.did = did
			self.cmd = cmd
			self.len = min(len(data) / 2, 16)
			self.data = data[0 : self.len * 2]

	def getPackageString(self):
		prefix = '55AA'
		sid = makeByte(self.sid)
		did = makeByte(self.did)

		data = self.data
		data = data + '00000000000000000000000000000000'[self.len*2:]

		pkgData = prefix + sid + did + makeWord(self.cmd) + makeWord(self.len) + data

		pkgData = pkgData + calcChkSum(pkgData)
		return pkgData

	def getPackageData(self):
		return binascii.unhexlify(self.getPackageString())

class CommandDataPackage(object):
	"""docstring for CommandDataPackage"""
	def __init__(self, 
			sid = 0,
			did = 0,
			cmd = 0,
			data = '',
			packageString = ''):
		super(CommandDataPackage, self).__init__()
		if (len(packageString) > 0):
			if not checkSum(packageString) : raise Exception("Checksum validation not valid!\r\nComposing data: " + packageString)
			self.sid = parseByte(packageString[2 * 2 : 3 * 2])
			self.did = parseByte(packageString[3 * 2 : 4 * 2])
			self.cmd = parseWord(packageString[4 * 2 : 6 * 2])
			self.len = min(parseWord(packageString[6 * 2 : 8 * 2]), 499)
			self.data = packageString[8 * 2 : 8 * 2 + self.len * 2]
		else:
			self.sid = sid
			self.did = did
			self.cmd = cmd
			self.len = min(len(data) / 2, 499)
			self.data = data[0 : self.len * 2]

	def getPackageString(self):
		prefix = '5AA5'
		sid = makeByte(self.sid)
		did = makeByte(self.did)

		data = self.data

		pkgData = prefix + sid + did + makeWord(self.cmd) + makeWord(self.len) + data

		pkgData = pkgData + calcChkSum(pkgData)
		return pkgData

	def getPackageData(self):
		return binascii.unhexlify(self.getPackageString())

class Session(object):
	"""docstring for Session"""
	def __init__(self):
		super(Session, self).__init__()

	def connect(self,
			port = "/dev/tty.SLAB_USBtoUART",
			baudrate = 115200,
			timeout = None):
		self.connection = getConnection(port, baudrate, timeout)
		if self.connection: return True
		else: return False

	def sendCommandMessage(self, cmd, data = ''):
		pkg = CommandPackage(cmd = cmd, data = data)
		return self.connection.write(pkg.getPackageData())
	def readResponseMessage(self):
		resData = self.connection.read(26)
		resPkg = ResponsePackage(packageString = resData.encode('hex'))
		return resPkg.ret, resPkg.data
	def sendCommandData(self, cmd, data = ''):
		pkg = CommandDataPackage(cmd = cmd, data = data)
		return self.connection.write(pkg.getPackageData())
	def readResponseData(self, data_len):
		resData = self.connection.read(12 + data_len)
		resPkg = ResponseDataPackage(packageString = resData.encode('hex'))
		return resPkg.ret, resPkg.data

	def sendCommand(self, cmd, cmd_data = "", data = None):
		self.sendCommandMessage(cmd, cmd_data)
		if data: self.sendCommandData(cmd, data)
	def readResponse(self, withData = True):
		res_ret, res_data = self.readResponseMessage()
		if res_ret != 0: return None
		if not withData: return res_data
		if len(res_data) != 4: return res_data
		data_len = parseWord(res_data)
		res_ret, res_data = self.readResponseData(data_len)
		return res_data

def makeWord(num, bigHead = False):
	if (num < 0) or (num > 65535): raise Exception('Not valid number')

	num_h = hex(num / 256)[2:]
	num_l = hex(num % 256)[2:]
	if len(num_h) == 1: num_h = '0' + num_h
	if len(num_l) == 1: num_l = '0' + num_l

	retStr = None
	if bigHead: retStr = num_h + num_l
	else: retStr = num_l + num_h

	return retStr.upper()

def parseWord(wordStr, bigHead = False):
	if len(wordStr) != 4: raise Exception('Not valid word string')
	word1 = wordStr[0:2]
	word2 = wordStr[2:4]
	word_h = '0'
	word_l = '0'
	if bigHead:
		word_h = word1
		word_l = word2
	else:
		word_l = word1
		word_h = word2
	return int(word_h, 16) * 256 + int(word_l, 16)

def makeByte(num):
	if (num < 0) or (num > 255): raise Exception('Not valid number')
	num_str = hex(num)[2:]
	if len(num_str) == 1: num_str = '0' + num_str
	return num_str.upper()

def parseByte(byte):
	if len(byte) != 2: raise Exception('Not valid byte string')
	return int(byte, 16)

def makeCommandPackage(cmd = '0000', data = '', packaged = True):
	prefix = 'AA55'
	sid = '00'
	did = '00'

	if type(cmd) == int: cmd = makeWord(cmd, True)

	data_len = len(data)
	if data_len % 2 != 0: raise Exception("Not valid data length")
	data_len = data_len / 2
	data = data + '00000000000000000000000000000000'[data_len*2:]

	pkgData = prefix + sid + did + cmd + makeWord(data_len) + data

	pkgData = pkgData + calcChkSum(pkgData)
	pkgDataPackaged = binascii.unhexlify(pkgData)
	return pkgDataPackaged, pkgData

def calcChkSum(data):
	_len = len(data)
	sum = 0
	for i in range(0, (_len)/2):
		_byte = data[i*2:i*2 + 2]
		_byte = int(_byte, 16)
		sum += _byte

	sum = hex(sum)
	sum = sum[2:]
	sum = sum[max(0,len(sum) - 4):]
	sum = int(sum, 16)

	return makeWord(sum)

def checkSum(data):
	_len = len(data)
	str_data = data[0:_len - 4]
	str_chk = data[_len - 4:_len]

	sum = calcChkSum(str_data)

	return sum == str_chk.upper()

def getConnection(
		port = "/dev/tty.SLAB_USBtoUART",
		baudrate = 115200,
		timeout = None):
	
	t = serial.Serial(port, baudrate, timeout = timeout)
	print(t.portstr)

	n = t.write(binascii.unhexlify('55AA000001000000000000000000000000000000000000000001'))
	s = t.read(26)

	conn_success =  s.encode('hex') == 'aa55010001000200000000000000000000000000000000000301'

	if (conn_success) :
		return t
	else :
		return None

def getDeviceInfo(session):
	session.sendCommand(4)
	return session.readResponse()

def detectFinger(session):
	return None